#include <protocolParseCommon.h>
const uint8_t parseFixedHead[PROTOCOL_FIXED_HEAD_SIZE] = {0X7A, 0XB5, 0X69}; // fixed head save

static void resetParseWork(struct protocolPackageParseInform *parseinform)
{
    if (!parseinform)
    {
        logerror("");
        return;
    }
    parseinform->parseState = protocolParseCommonStep_FixedHead;
    parseinform->parseReceiveSize = 0;
}

static uint32_t protocolCalculatePackHeadSize(struct protocolPackageCommonInform *inpack)
{
    uint32_t tmpsize = 0;
    if (!inpack)
    {
        logerror("");
        return tmpsize;
    }
    switch (inpack->protocolVersion)
    {
    case protocolParseCommonVersion0:
    {
        tmpsize = offsetof(struct protocolPackageCommonInform, v0.v0Data);
        break;
    }
    case protocolParseCommonVersion1:
    {
        tmpsize = offsetof(struct protocolPackageCommonInform, v1.v1Data);
        break;
    }
    case protocolParseCommonVersion2:
    {
        tmpsize = offsetof(struct protocolPackageCommonInform, pv2.v2Data);
        break;
    }
    case protocolParseCommonVersion3:
    {
        tmpsize = offsetof(struct protocolPackageCommonInform, v3.v3Data);
        break;
    }
    default:
    {
        logerror("");
        break;
    }
    }
    return tmpsize;
}
static uint32_t protocolCalculatePackTotalSize(struct protocolPackageCommonInform *inpack)
{
    uint32_t tmpsize = 0;
    if (!inpack)
    {
        logerror("");
        return tmpsize;
    }
    switch (inpack->protocolVersion)
    {
    case protocolParseCommonVersion0:
    {
        tmpsize = offsetof(struct protocolPackageCommonInform, v0.v0Data) + inpack->v0.v0DataSize;
        break;
    }
    case protocolParseCommonVersion1:
    {
        tmpsize = offsetof(struct protocolPackageCommonInform, v1.v1Data) + inpack->v1.v1DataSize;
        break;
    }
    case protocolParseCommonVersion2:
    {
        tmpsize = offsetof(struct protocolPackageCommonInform, pv2.v2Data) + inpack->pv2.v2DataSize;
        break;
    }
    case protocolParseCommonVersion3:
    {
        tmpsize = offsetof(struct protocolPackageCommonInform, v3.v3Data) + inpack->v3.v3DataSize;
        break;
    }
    default:
    {
        logerror("");
        break;
    }
    }
    return tmpsize;
}
static int protocolComparePackCrc(struct protocolPackageCommonInform *inpack)
{
    if (!inpack)
    {
        logerror("");
        return -1;
    }
    switch (inpack->protocolVersion)
    {
    case protocolParseCommonVersion0:
    { // ok, no crc
        return 0;
    }
    case protocolParseCommonVersion1:
    {
        uint8_t tmpcrc = calculateUint8CrcHash(inpack->v1.v1Data, inpack->v1.v1DataSize);
        if (tmpcrc == inpack->v1.v1Crc)
        { // ok
            return 0;
        }
        logerror("%x:%x", tmpcrc, inpack->v1.v1Crc);
        return -1;
    }
    case protocolParseCommonVersion2:
    {
        uint8_t tmpcrc = calculateUint8CrcHash(inpack->pv2.v2Data, inpack->pv2.v2DataSize);
        if (tmpcrc == inpack->pv2.v2Crc)
        { // ok
            return 0;
        }
        logerror("%x:%x", tmpcrc, inpack->pv2.v2Crc);
        return -1;
    }
    case protocolParseCommonVersion3:
    {
        uint8_t tmpcrc = calculateUint8CrcHash(inpack->v3.v3Data, inpack->v3.v3DataSize);
        if (tmpcrc == inpack->v3.v3Crc)
        { // ok
            return 0;
        }
        logerror("%x:%x", tmpcrc, inpack->v3.v3Crc);
        return -1;
    }
    default:
    {
        logerror("");
        break;
    }
    }
    return -1;
}

int protocolPackageParseInit(struct protocolPackageParseInform *parseinform,
                             protocolParseCallback setCallback, void *setpack, uint32_t setMaxPackageSize)
{
    if (!parseinform)
    {
        logerror("");
        return -1;
    }
    if (!setpack || setMaxPackageSize < sizeof(struct protocolPackageCommonInform))
    {
        logerror("");
        return -1;
    }
    parseinform->parseCallback = setCallback;
    parseinform->packinform = setpack;
    parseinform->parseMaxPackSize = setMaxPackageSize;
    resetParseWork(parseinform);
    return 0;
}

uint32_t protocolPackageDataV0(const void *packData, uint32_t packsize, void *outPack, uint32_t outsize)
{
    struct protocolPackageCommonInform *tmppack = (struct protocolPackageCommonInform *)outPack;
    if (!outPack)
    {
        logerror("");
        return 0;
    }
    memcpy(tmppack->protocolFixedHead, parseFixedHead, PROTOCOL_FIXED_HEAD_SIZE);
    tmppack->protocolVersion = protocolParseCommonVersion0;
    tmppack->v0.v0DataSize = packsize;
    uint32_t tmpTotalSize = protocolCalculatePackTotalSize(tmppack);
    if (tmpTotalSize > outsize)
    {
        logerror("%u,%u", tmpTotalSize, outsize);
        return 0;
    }
    memcpy(tmppack->v0.v0Data, packData, packsize);
    return tmpTotalSize;
}
uint32_t protocolPackageDataV1(const void *packData, uint32_t packsize, void *outPack, uint32_t outsize,
                               uint8_t packCrypteType, uint8_t packCompressType, uint8_t packDataType)
{
    struct protocolPackageCommonInform *tmppack = (struct protocolPackageCommonInform *)outPack;
    if (!outPack)
    {
        logerror("");
        return 0;
    }
    memcpy(tmppack->protocolFixedHead, parseFixedHead, PROTOCOL_FIXED_HEAD_SIZE);
    tmppack->protocolVersion = protocolParseCommonVersion1;
    tmppack->v1.v1DataSize = packsize;
    tmppack->v1.v1DataCrypteType = packCrypteType;
    tmppack->v1.v1DataCompressType = packCompressType;
    tmppack->v1.v1DataType = packDataType;
    uint32_t tmpTotalSize = protocolCalculatePackTotalSize(tmppack);
    if (tmpTotalSize > outsize)
    {
        logerror("%u,%u", (uint32_t)packsize, outsize);
        return 0;
    }
    tmppack->v1.v1Crc = calculateUint8CrcHash((const uint8_t *)packData, packsize);
    memcpy(tmppack->v1.v1Data, packData, packsize);
    return tmpTotalSize;
}
uint16_t protocolPackageDataV2(void *outPack, uint16_t outsize)
{
    struct protocolPackageCommonInform *tmppack = (struct protocolPackageCommonInform *)outPack;
    if (!outPack)
    {
        logerror("");
        return 0;
    }
    memcpy(tmppack->protocolFixedHead, parseFixedHead, PROTOCOL_FIXED_HEAD_SIZE);
    tmppack->protocolVersion = protocolParseCommonVersion2;
    uint16_t tmpTotalSize = protocolCalculatePackTotalSize(tmppack);
    if (tmpTotalSize > outsize)
    {
        logerror("%u,%u", tmppack->pv2.v2DataSize, outsize);
        return 0;
    }
    tmppack->pv2.v2Crc = calculateUint8CrcHash((const uint8_t *)tmppack->pv2.v2Data, tmppack->pv2.v2DataSize);
    return tmpTotalSize;
}
uint16_t protocolPackageDataV3(void *outPack, uint16_t outsize)
{
    struct protocolPackageCommonInform *tmppack = (struct protocolPackageCommonInform *)outPack;
    if (!outPack)
    {
        logerror("");
        return 0;
    }
    memcpy(tmppack->protocolFixedHead, parseFixedHead, PROTOCOL_FIXED_HEAD_SIZE);
    tmppack->protocolVersion = protocolParseCommonVersion3;
    uint16_t tmpTotalSize = protocolCalculatePackTotalSize(tmppack);
    if (tmpTotalSize > outsize)
    {
        logerror("%u,%u", tmppack->v3.v3DataSize, outsize);
        return 0;
    }
    tmppack->v3.v3Crc = calculateUint8CrcHash((const uint8_t *)tmppack->v3.v3Data, tmppack->v3.v3DataSize);
    return tmpTotalSize;
}
void protocolPackageParseValue(struct protocolPackageParseInform *parseinform, uint8_t indata)
{
    switch (parseinform->parseState)
    {
    case protocolParseCommonStep_FixedHead:
    {
        if (parseinform->parseReceiveSize < PROTOCOL_FIXED_HEAD_SIZE)
        {
            if (indata == parseFixedHead[parseinform->parseReceiveSize])
            {

                parseinform->packbuffer[parseinform->parseReceiveSize++] = indata;
            }
            else
            {
                parseinform->parseReceiveSize = 0;
                if (indata == parseFixedHead[parseinform->parseReceiveSize])
                {
                    parseinform->packbuffer[parseinform->parseReceiveSize++] = indata;
                }
            }
        }
        if (parseinform->parseReceiveSize >= PROTOCOL_FIXED_HEAD_SIZE)
        {
            parseinform->parseState = protocolParseCommonStep_Version;
        }
        break;
    }
    case protocolParseCommonStep_Version:
    {
        parseinform->packbuffer[parseinform->parseReceiveSize++] = indata;
        parseinform->parseHeadSize = protocolCalculatePackHeadSize(parseinform->packinform);
        if (!parseinform->parseHeadSize)
        {
            logerror("parseVersion:%d", parseinform->packinform->protocolVersion);
            resetParseWork(parseinform);
            break;
        }
        parseinform->parseState = protocolParseCommonStep_AllHead;
        break;
    }
    case protocolParseCommonStep_AllHead:
    {
        if (parseinform->parseReceiveSize < parseinform->parseHeadSize)
        {
            parseinform->packbuffer[parseinform->parseReceiveSize++] = indata;
        }
        if (parseinform->parseReceiveSize < parseinform->parseHeadSize)
        {
            break;
        }
        parseinform->parseTotalSize = protocolCalculatePackTotalSize(parseinform->packinform);
        if (!parseinform->parseTotalSize)
        {
            logerror("parseVersion:%d", parseinform->packinform->protocolVersion);
            resetParseWork(parseinform);
            break;
        }
        if (parseinform->parseTotalSize > parseinform->parseMaxPackSize)
        {
            logerror("%u,%u", parseinform->parseTotalSize, parseinform->parseMaxPackSize);
            resetParseWork(parseinform);
            break;
        }
        parseinform->parseState = protocolParseCommonStep_Data;
        break;
    }
    case protocolParseCommonStep_Data:
    {
        if (parseinform->parseReceiveSize < parseinform->parseTotalSize)
        {
            parseinform->packbuffer[parseinform->parseReceiveSize++] = indata;
        }
        if (parseinform->parseReceiveSize < parseinform->parseTotalSize)
        {
            break;
        }
        if (protocolComparePackCrc(parseinform->packinform) < 0)
        {
            logerror("");
            resetParseWork(parseinform);
            break;
        }
        if (parseinform->parseCallback)
        {
            parseinform->parseCallback(parseinform->packbuffer, parseinform->parseTotalSize, parseinform);
        }
    }
    default:
        resetParseWork(parseinform);
        break;
    }
}
